package com.stars.easyms.base.util;

import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.lang.NonNull;
import org.springframework.lang.Nullable;
import org.springframework.util.ClassUtils;
import org.springframework.web.context.support.GenericWebApplicationContext;

/**
 * 以静态变量保存Spring ApplicationContext, 可在任何代码任何地方任何时候中取出ApplicaitonContext.
 *
 * @author guoguifang
 * @date 2018-10-19 10:07
 * @since 1.0.0
 */
public final class ApplicationContextHolder {

    private static final Logger logger = LoggerFactory.getLogger(ApplicationContextHolder.class);

    private static ApplicationContext applicationContext;

    /**
     * 1.x.x版本和2.x.x版本对ApplicationContext重构了
     * <p>
     * 1.x.x版本注入的是:
     * org.springframework.context.annotation.AnnotationConfigApplicationContext
     * org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext
     * <p>
     * 2.x.x版本注入的是:
     * org.springframework.context.annotation.AnnotationConfigApplicationContext
     * org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext
     * org.springframework.boot.web.reactive.context.AnnotationConfigReactiveWebServerApplicationContext
     * <p>
     * 有可能会同时注入多个，同类型的后面覆盖前面的，不同类型的优先级：
     * AnnotationConfigEmbeddedWebApplicationContext > AnnotationConfigApplicationContext
     * AnnotationConfigServletWebServerApplicationContext > AnnotationConfigReactiveWebServerApplicationContext > AnnotationConfigApplicationContext
     *
     * @param applicationContext applicationContext
     */
    public static void setApplicationContext(ApplicationContext applicationContext) {
        if (applicationContext != null) {
            boolean isSetApplicationContext = false;
            if (ApplicationContextHolder.applicationContext == null) {
                isSetApplicationContext = true;
            } else if (ApplicationContextHolder.applicationContext != applicationContext) {
                if (applicationContext instanceof GenericWebApplicationContext) {
                    isSetApplicationContext = true;
                } else if (!(ApplicationContextHolder.applicationContext instanceof GenericWebApplicationContext)) {
                    Class<?> genericReactiveWebApplicationContextClass = getGenericReactiveWebApplicationContextClass();
                    isSetApplicationContext = genericReactiveWebApplicationContextClass == null ||
                            genericReactiveWebApplicationContextClass.isInstance(applicationContext);
                }
            }
            if (isSetApplicationContext) {
                ApplicationContextHolder.applicationContext = applicationContext;
                PropertyPlaceholderUtil.setEnvironment(applicationContext.getEnvironment());
            }
        }
    }

    @NonNull
    public static ApplicationContext getApplicationContext() {
        if (applicationContext == null) {
            throw new IllegalStateException("ApplicationContext is null!");
        }
        return applicationContext;
    }

    @Nullable
    public static Object getBean(String beanId) {
        if (applicationContext != null && StringUtils.isNotBlank(beanId)) {
            try {
                return applicationContext.getBean(beanId);
            } catch (BeansException e) {
                if (logger.isDebugEnabled()) {
                    logger.debug("Bean failed to get beanId '{}'!", beanId);
                }
            }
        }
        return null;
    }

    @Nullable
    public static <T> T getBean(Class<T> beanClass) {
        if (applicationContext != null && beanClass != null) {
            try {
                return applicationContext.getBean(beanClass);
            } catch (BeansException e) {
                if (logger.isDebugEnabled()) {
                    logger.debug("Bean failed to get bean class '{}'!", beanClass.getName());
                }
            }
        }
        return null;
    }

    /**
     * 获取GenericReactiveWebApplicationContext类，如果是1.x.x版本不存在返回null
     */
    private static Class<?> getGenericReactiveWebApplicationContextClass() {
        try {
            return ClassUtils.forName("org.springframework.boot.web.reactive.context.GenericReactiveWebApplicationContext",
                    ClassUtils.getDefaultClassLoader());
        } catch (ClassNotFoundException e) {
            return null;
        }
    }

    private ApplicationContextHolder() {
    }
}
